package com.tyut.musicdreamer.framework.starter.storage.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.model.*;
import com.tyut.musicdreamer.framework.starter.storage.config.StorageProperties;
import com.tyut.musicdreamer.framework.starter.storage.model.StorageClass;
import com.tyut.musicdreamer.framework.starter.storage.model.UploadResult;
import com.tyut.musicdreamer.framework.starter.storage.service.StorageService;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.UUID;

/**
 * 阿里云OSS存储服务实现
 */
@Slf4j
@RequiredArgsConstructor
public class AliyunOssStorageServiceImpl implements StorageService {

    private final StorageProperties properties;

    @PostConstruct
    public void init() {
        log.info("=== AliyunOssStorageServiceImpl 初始化 ===");
        log.info("OSS端点: {}", properties.getOss().getEndpoint());
        log.info("OSS存储桶: {}", properties.getOss().getBucketName());
        
        // 打印允许的文件类型
        if (properties.getAllowedTypes() != null) {
            log.info("允许的文件类型: {}", properties.getAllowedTypes());
        } else {
            log.warn("未配置允许的文件类型");
        }
        
        log.info("最大文件大小: {}", properties.getMaxSize());
        log.info("======================================");
    }

    /**
     * 获取OSS客户端
     */
    private OSS getOssClient() {
        return new OSSClientBuilder().build(
                properties.getOss().getEndpoint(),
                new DefaultCredentialProvider(
                        properties.getOss().getAccessKeyId(),
                        properties.getOss().getAccessKeySecret()
                )
        );
    }

    @Override
    public UploadResult store(MultipartFile file, String directory) throws IOException {
        log.info("OSS存储服务: 开始存储文件到目录: {}, 文件名: {}, 内容类型: {}, 大小: {}字节", 
            directory, file.getOriginalFilename(), file.getContentType(), file.getSize());
        return store(file, directory, StorageClass.STANDARD);
    }
    
    @Override
    public UploadResult store(MultipartFile file, String directory, StorageClass storageClass) throws IOException {
        log.info("OSS存储服务: 开始存储文件到目录: {}, 文件名: {}, 内容类型: {}, 存储类型: {}", 
            directory, file.getOriginalFilename(), file.getContentType(), storageClass);
        // 验证文件
        validateFile(file);

        // 存储文件
        return storeFile(file.getInputStream(), file.getOriginalFilename(), file.getContentType(), file.getSize(), directory, storageClass);
    }

    @Override
    public UploadResult store(InputStream inputStream, String fileName, String contentType, long fileSize, String directory) throws IOException {
        return storeFile(inputStream, fileName, contentType, fileSize, directory, StorageClass.STANDARD);
    }
    
    @Override
    public UploadResult store(InputStream inputStream, String fileName, String contentType, long fileSize, String directory, StorageClass storageClass) throws IOException {
        return storeFile(inputStream, fileName, contentType, fileSize, directory, storageClass);
    }

    /**
     * 存储文件的内部实现
     */
    private UploadResult storeFile(InputStream inputStream, String fileName, String contentType, long fileSize, String directory, StorageClass storageClass) throws IOException {
        // 生成唯一文件名
        String originalFilename = StrUtil.isNotBlank(fileName) ? fileName : "unknown";
        String extension = FileUtil.extName(originalFilename);
        String objectKey = buildObjectKey(directory, extension);

        OSS ossClient = getOssClient();
        try {
            // 设置元数据
            ObjectMetadata metadata = new ObjectMetadata();
            
            // 设置正确的内容类型
            if (contentType != null) {
                metadata.setContentType(contentType);
                
                // 特殊处理音频文件
                if (contentType.startsWith("audio/")) {
                    log.info("检测到音频文件: {}, 类型: {}", originalFilename, contentType);
                    // 对于音频文件，确保正确设置内容类型
                    if ("mpeg".equals(contentType.substring(contentType.indexOf("/") + 1)) || 
                        "mp3".equals(extension)) {
                        metadata.setContentType("audio/mpeg");
                    }
                }
            }
            
            metadata.setContentLength(fileSize);
            
            // 设置存储类型
            metadata.setHeader("x-oss-storage-class", convertStorageClass(storageClass));

            // 上传到OSS
            log.info("开始上传文件到OSS: bucket={}, objectKey={}, contentType={}", 
                properties.getOss().getBucketName(), objectKey, metadata.getContentType());
                
            ossClient.putObject(
                    properties.getOss().getBucketName(),
                    objectKey,
                    inputStream,
                    metadata
            );
            log.info("文件上传到OSS成功: {}", objectKey);

            // 生成访问URL
            String fileUrl = getFileUrl(objectKey);
            
            // 生成CDN URL
            String cdnUrl = getCdnUrl(objectKey);
            
            // 生成预签名URL (1小时有效)
            String presignedUrl = generatePresignedUrl(objectKey, 60);

            // 构建上传结果
            return UploadResult.builder()
                    .objectKey(objectKey)
                    .fileName(originalFilename)
                    .fileSize(fileSize)
                    .contentType(metadata.getContentType()) // 使用可能已更新的内容类型
                    .url(fileUrl)
                    .cdnUrl(cdnUrl)
                    .presignedUrl(presignedUrl)
                    .build();
        } catch (Exception e) {
            log.error("上传文件到OSS失败: {}, 错误: {}", objectKey, e.getMessage(), e);
            throw new IOException("上传文件失败: " + e.getMessage(), e);
        } finally {
            ossClient.shutdown();
        }
    }

    @Override
    public String generatePresignedUrl(String objectKey, int expirationMinutes) {
        if (StrUtil.isBlank(objectKey)) {
            return null;
        }

        OSS ossClient = getOssClient();
        try {
            // 设置过期时间
            Date expiration = new Date(System.currentTimeMillis() + expirationMinutes * 60 * 1000);

            // 生成预签名下载URL
            URL url = ossClient.generatePresignedUrl(
                    properties.getOss().getBucketName(),
                    objectKey,
                    expiration
            );

            return url.toString();
        } finally {
            ossClient.shutdown();
        }
    }

    @Override
    public UploadResult generatePresignedUploadUrl(String fileName, String contentType, String directory, int expirationMinutes) {
        return generatePresignedUploadUrl(fileName, contentType, directory, StorageClass.STANDARD, expirationMinutes);
    }
    
    @Override
    public UploadResult generatePresignedUploadUrl(String fileName, String contentType, String directory, StorageClass storageClass, int expirationMinutes) {
        OSS ossClient = getOssClient();
        try {
            // 生成唯一文件名
            String extension = FileUtil.extName(fileName);
            String objectKey = buildObjectKey(directory, extension);

            // 设置元数据
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentType(contentType);
            metadata.setHeader("x-oss-storage-class", convertStorageClass(storageClass));

            // 设置过期时间
            Date expiration = new Date(System.currentTimeMillis() + expirationMinutes * 60 * 1000);

            // 生成预签名上传URL
            URL url = ossClient.generatePresignedUrl(
                    properties.getOss().getBucketName(),
                    objectKey,
                    expiration
            );

            // 生成CDN URL
            String cdnUrl = getCdnUrl(objectKey);

            // 构建上传结果
            return UploadResult.builder()
                    .objectKey(objectKey)
                    .fileName(fileName)
                    .contentType(contentType)
                    .url(getFileUrl(objectKey))
                    .cdnUrl(cdnUrl)
                    .presignedUrl(url.toString())
                    .build();
        } finally {
            ossClient.shutdown();
        }
    }

    @Override
    public boolean delete(String objectKey) {
        if (StrUtil.isBlank(objectKey)) {
            return false;
        }

        OSS ossClient = getOssClient();
        try {
            ossClient.deleteObject(
                    properties.getOss().getBucketName(),
                    objectKey
            );
            return true;
        } catch (Exception e) {
            log.error("删除文件失败: {}", objectKey, e);
            return false;
        } finally {
            ossClient.shutdown();
        }
    }

    @Override
    public InputStream getFileInputStream(String objectKey) throws IOException {
        if (StrUtil.isBlank(objectKey)) {
            log.warn("对象键为空");
            return null;
        }

        OSS ossClient = getOssClient();
        try {
            OSSObject ossObject = ossClient.getObject(
                    properties.getOss().getBucketName(),
                    objectKey
            );
            
            // 注意：调用方需要负责关闭输入流和OSS客户端
            return ossObject.getObjectContent();
        } catch (Exception e) {
            log.error("获取文件输入流失败: {}", e.getMessage());
            throw new IOException("获取文件输入流失败", e);
        }
    }

    @Override
    public String getFileUrl(String objectKey) {
        if (StrUtil.isBlank(objectKey)) {
            return null;
        }

        // 生成预签名URL以确保私有文件可访问 (默认24小时有效)
        return generatePresignedUrl(objectKey, 24 * 60);
    }
    
    @Override
    public String getCdnUrl(String objectKey) {
        if (StrUtil.isBlank(objectKey)) {
            return null;
        }

        // 如果已配置CDN域名，使用CDN URL
        if (StrUtil.isNotBlank(properties.getOss().getCdnDomain())) {
            return "https://" + properties.getOss().getCdnDomain() + "/" + objectKey;
        }
        
        // 否则返回普通URL
        return getFileUrl(objectKey);
    }
    
    @Override
    public boolean updateStorageClass(String objectKey, StorageClass storageClass) {
        if (StrUtil.isBlank(objectKey)) {
            return false;
        }
        
        OSS ossClient = getOssClient();
        try {
            // 使用复制操作更新存储类型
            CopyObjectRequest request = new CopyObjectRequest(
                    properties.getOss().getBucketName(),
                    objectKey,
                    properties.getOss().getBucketName(),
                    objectKey
            );
            
            ObjectMetadata newMetadata = new ObjectMetadata();
            newMetadata.setHeader("x-oss-storage-class", convertStorageClass(storageClass));
            request.setNewObjectMetadata(newMetadata);
            
            ossClient.copyObject(request);
            
            log.info("更新存储类别成功, 对象: {}, 存储类别: {}", objectKey, storageClass);
            return true;
        } catch (Exception e) {
            log.error("更新存储类别失败: {}", objectKey, e);
            return false;
        } finally {
            ossClient.shutdown();
        }
    }

    /**
     * 验证文件
     */
    private void validateFile(MultipartFile file) {
        // 验证文件是否为空
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("文件不能为空");
        }

        // 验证文件类型
        String contentType = file.getContentType();
        if (contentType == null) {
            throw new IllegalArgumentException("无法识别的文件类型");
        }
        
        // 特殊处理：始终允许音频和图片文件
        if (contentType.startsWith("audio/") || contentType.startsWith("image/")) {
            log.info("允许的媒体类型: {}", contentType);
            return;  // 直接返回，跳过后续检查
        }
        
        // 检查是否配置了允许的类型
        if (properties.getAllowedTypes() != null && !properties.getAllowedTypes().isEmpty()) {
            boolean allowed = false;
            
            // 检查完全匹配
            if (properties.getAllowedTypes().contains(contentType)) {
                allowed = true;
            } else {
                // 检查MIME类型前缀匹配 (如audio/*)
                String mimePrefix = contentType.split("/")[0] + "/*";
                if (properties.getAllowedTypes().contains(mimePrefix)) {
                    allowed = true;
                }
            }
            
            if (!allowed) {
                log.error("不支持的文件类型: {}, 允许的类型: {}", contentType, properties.getAllowedTypes());
                throw new IllegalArgumentException("不支持的文件类型: " + contentType);
            }
        }

        // 验证文件大小
        if (properties.getMaxSize() != null && file.getSize() > properties.getMaxSize()) {
            throw new IllegalArgumentException("文件大小超过限制: " + file.getSize() + " > " + properties.getMaxSize());
        }
    }

    /**
     * 构建对象键
     */
    private String buildObjectKey(String directory, String extension) {
        // 确保目录以/结尾
        String dir = StrUtil.isNotBlank(directory) ? directory : "";
        if (StrUtil.isNotBlank(dir) && !dir.endsWith("/")) {
            dir = dir + "/";
        }

        // 生成UUID作为文件名
        String uuid = UUID.randomUUID().toString();
        
        // 构建对象键
        return dir + uuid + (StrUtil.isNotBlank(extension) ? "." + extension : "");
    }
    
    /**
     * 转换存储类别为阿里云OSS存储类别
     */
    private String convertStorageClass(StorageClass storageClass) {
        if (storageClass == null) {
            return com.aliyun.oss.model.StorageClass.Standard.toString();
        }
        
        return switch (storageClass) {
            case STANDARD -> com.aliyun.oss.model.StorageClass.Standard.toString();
            case IA -> com.aliyun.oss.model.StorageClass.IA.toString();
            case ARCHIVE -> com.aliyun.oss.model.StorageClass.Archive.toString();
            case COLD_ARCHIVE -> com.aliyun.oss.model.StorageClass.ColdArchive.toString();
            default -> com.aliyun.oss.model.StorageClass.Standard.toString();
        };
    }

    /**
     * 生成RTMP推流URL
     *
     * @param channelName 通道名称
     * @param expirationMinutes 过期时间(分钟)
     * @return RTMP推流URL
     */
    @Override
    public String generateRtmpUrl(String channelName, int expirationMinutes) {
        OSS ossClient = getOssClient();
        try {
            // 设置过期时间
            long expireTime = System.currentTimeMillis() / 1000 + expirationMinutes * 60;
            
            // 生成RTMP推流URL
            String rtmpUrl = ossClient.generateRtmpUri(
                    properties.getOss().getBucketName(),
                    channelName,
                    "playlist.m3u8",
                    expireTime
            );
            
            log.info("生成RTMP推流URL成功: {}", rtmpUrl);
            return rtmpUrl;
        } catch (Exception e) {
            log.error("生成RTMP推流URL失败: {}", e.getMessage(), e);
            return null;
        } finally {
            ossClient.shutdown();
        }
    }
    
    /**
     * 创建RTMP直播通道
     *
     * @param channelName 通道名称
     * @param description 通道描述
     * @return 推流URL和播放URL
     */
    @Override
    public String[] createLiveChannel(String channelName, String description) {
        OSS ossClient = getOssClient();
        try {
            // 创建LiveChannel请求
            CreateLiveChannelRequest request = new CreateLiveChannelRequest(
                    properties.getOss().getBucketName(),
                    channelName,
                    description,
                    LiveChannelStatus.Enabled,
                    new LiveChannelTarget()
            );
            
            // 创建LiveChannel
            CreateLiveChannelResult result = ossClient.createLiveChannel(request);
            
            // 获取推流地址和播放地址
            String publishUrl = result.getPublishUrls().get(0);
            String playUrl = result.getPlayUrls().get(0);
            
            log.info("创建RTMP直播通道成功: 通道名称={}, 推流地址={}, 播放地址={}", 
                channelName, publishUrl, playUrl);
                
            return new String[] { publishUrl, playUrl };
        } catch (Exception e) {
            log.error("创建RTMP直播通道失败: {}", e.getMessage(), e);
            return null;
        } finally {
            ossClient.shutdown();
        }
    }
} 